<template>
	<div>
		<h2>振荡器</h2>
		<el-radio-group v-model="radioValue0_0">
			<el-radio label="sine">sine</el-radio>
			<el-radio label="square">square</el-radio>
			<el-radio label="sawtooth">sawtooth</el-radio>
			<el-radio label="triangle">triangle</el-radio>
		</el-radio-group>
		<el-slider v-model="sliderValue0_0" :step="0.01" :min="20" :max="4000"></el-slider>
		<el-slider v-model="sliderValue0_1" :step="0.01" :min="0" :max="1000"></el-slider>
		<div>
			<el-button @click="media0ResumeEventHandel">resume</el-button>
			<el-button @click="media0OscillatorEventHandel">{{oscillatorStart ? 'stop' : 'start'}}</el-button>
			<audio style="display: block;" ref="audioElement0" :src="demoSrc" preload="auto" controls loop></audio>
		</div>
	</div>
	<div>
		<h2>音量</h2>
		<el-slider v-model="sliderValue1_0" :step="0.01" :min="0" :max="4"></el-slider>
		<el-slider v-model="sliderValue1_1" :min="0" :max="4"></el-slider>
		<div>
			<el-button @click="media1ResumeEventHandel">resume</el-button>
			<audio style="display: block;" ref="audioElement1" :src="demoSrc" preload="auto" controls loop></audio>
		</div>
	</div>
	<div>
		<h2>立体声平移</h2>
		<el-slider v-model="sliderValue2_0" :step="0.01" :min="-1" :max="1"></el-slider>
		<div>
			<el-button @click="media2ResumeEventHandel">resume</el-button>
			<audio style="display: block;" ref="audioElement2" :src="demoSrc" preload="auto" controls loop></audio>
		</div>
	</div>
	<div>
		<h2>分析</h2>
		<el-radio-group v-model="audio3Type">
			<el-radio label="floatFrequency">floatFrequency</el-radio>
			<el-radio label="byteFrequency">byteFrequency</el-radio>
			<el-radio label="floatTimeDomain">floatTimeDomain</el-radio>
			<el-radio label="byteTimeDomain">byteTimeDomain</el-radio>
		</el-radio-group>
		<canvas ref="canvasElement3_0" style="background: #000;width: 500px;height: 300px;margin-bottom: 10px;display: block;"></canvas>
		<div>
			<el-button @click="media3ResumeEventHandel">resume</el-button>
			<el-button @click="audio3Pause = !audio3Pause">{{audio3Pause ? '开始绘画' : '暂停绘画'}}</el-button>
			<audio style="display: block;" ref="audioElement3" :src="demoSrc" preload="auto" controls loop></audio>
		</div>
	</div>
	<div>
		<h2>滤波器</h2>
		<el-radio-group v-model="audio4Type">
			<el-radio label="lowpass">lowpass</el-radio>
			<el-radio label="highpass">highpass</el-radio>
			<el-radio label="bandpass">bandpass</el-radio>
			<el-radio label="lowshelf">lowshelf</el-radio>
			<el-radio label="highshelf">highshelf</el-radio>
			<el-radio label="peaking">peaking</el-radio>
			<el-radio label="notch">notch</el-radio>
			<el-radio label="allpass">allpass</el-radio>
		</el-radio-group>
		<el-slider v-model="audio4Frequency" :step="0.01" :min="20" :max="4000"></el-slider>
		<el-slider v-model="audio4Q" :step="0.01" :min="0.0001" :max="1000"></el-slider>
		<el-slider v-model="audio4Gain" :step="0.01" :min="-40" :max="40"></el-slider>
		<div>
			<el-button @click="media4ResumeEventHandel">resume</el-button>
			<audio style="display: block;" ref="audioElement4" :src="demoSrc" preload="auto" controls loop></audio>
		</div>
	</div>
	<div>
		<h2>白噪音</h2>
		<div>
			<el-button @click="media5ResumeEventHandel">resume</el-button>
			<el-button @click="media5PlayWhiteNoise">{{audio5Pause ? '播放10秒白噪音' : '暂停'}}</el-button>
			<el-button @click="media5ResetWhiteNoise">停止还原</el-button>
		</div>
	</div>
	<div>
		<h2>3D立体音</h2>
		<el-slider v-model="audio6PannerX" :min="-1000" :max="1000"></el-slider>
		<el-slider v-model="audio6PannerY" :min="-1000" :max="1000"></el-slider>
		<el-slider v-model="audio6PannerZ" :min="-1000" :max="1000"></el-slider>
		<div>
			<el-button @click="media6ResumeEventHandel">resume</el-button>
			<audio style="display: block;" ref="audioElement6" :src="demoSrc" preload="auto" controls loop></audio>
		</div>
	</div>
	<div>
		<h2>多声道</h2>
		<el-slider v-model="audio7GainL" :step="0.01" :min="0" :max="2"></el-slider>
		<el-slider v-model="audio7GainR" :step="0.01" :min="0" :max="2"></el-slider>
		<div>
			<el-button @click="media7ResumeEventHandel">resume</el-button>
			<el-button @click="media7ChangeLRChannel">{{audio7LRChannel ? '左右' : '右左'}}</el-button>
			<audio style="display: block;" ref="audioElement7" :src="demoSrc" preload="auto" controls loop></audio>
		</div>
	</div>
</template>

<script setup>
	
	import { ref, onMounted, watch } from 'vue';
	import demoSrc from '@/assets/audio/demo.mp3';
	
	const audioElement0 = ref();
	const radioValue0_0 = ref('sine');
	const sliderValue0_0 = ref(440);
	const sliderValue0_1 = ref(0);
	const auer0 = new AudioContext();
	const oscillatorStart = ref(false);
	let track0, oscillator0_0, freq0_0, detune0_0;
	
	function media0ResumeEventHandel() {
		if (auer0.state === "suspended") {
			auer0.resume().then(() => {
				track0 = auer0.createMediaElementSource(audioElement0.value);
				track0.connect(auer0.destination);
			});
		}
	}
	
	function media0OscillatorEventHandel() {
		if (oscillatorStart.value) {
			oscillator0_0.stop();
			oscillator0_0.disconnect(auer0.destination);
		} else {
			oscillator0_0 = auer0.createOscillator({ type: radioValue0_0.value, frequency: sliderValue0_0.value, detune: sliderValue0_1.value });
			oscillator0_0.connect(auer0.destination);
			oscillator0_0.start();
		}
		oscillatorStart.value = !oscillatorStart.value;
	}
	
	watch(radioValue0_0, newVal => oscillator0_0 && (oscillator0_0.type = newVal));
	watch(sliderValue0_0, newVal => oscillator0_0 && (oscillator0_0.frequency.value = newVal));
	watch(sliderValue0_1, newVal => oscillator0_0 && (oscillator0_0.detune.value = newVal));
	
	const audioElement1 = ref();
	const sliderValue1_0 = ref(1);
	const sliderValue1_1 = ref(4);
	const auer1 = new AudioContext();
	let track1, gain1_0, gain1_1;
	
	function media1ResumeEventHandel() {
		if (auer1.state === "suspended") {
			auer1.resume().then(() => {
				track1 = auer1.createMediaElementSource(audioElement1.value);
				gain1_0 = auer1.createGain();
				gain1_1 = auer1.createGain();
				gain1_0.gain.value = sliderValue1_0.value;
				gain1_1.gain.value = sliderValue1_1.value;
				track1.connect(gain1_0);
				gain1_0.connect(gain1_1);
				gain1_1.connect(auer1.destination);
			});
		}
	}
	
	watch(sliderValue1_0, newVal => gain1_0 && (gain1_0.gain.value = newVal));
	watch(sliderValue1_1, newVal => gain1_1 && gain1_1.gain.setValueAtTime(newVal, auer1.currentTime + 1));
	
	const audioElement2 = ref();
	const sliderValue2_0 = ref(1);
	const auer2 = new AudioContext();
	let track2, pan2_0;
	
	function media2ResumeEventHandel() {
		if (auer2.state === "suspended") {
			auer2.resume().then(() => {
				track2 = auer2.createMediaElementSource(audioElement2.value);
				pan2_0 = auer2.createStereoPanner();
				pan2_0.pan.value = sliderValue2_0.value;
				track2.connect(pan2_0);
				pan2_0.connect(auer2.destination);
			});
		}
	}
	
	watch(sliderValue2_0, newVal => pan2_0 && (pan2_0.pan.value = newVal));
	
	const audioElement3 = ref();
	const auer3 = new AudioContext();
	const audio3Type = ref('floatFrequency');
	const audio3Pause = ref(true);
	const canvasElement3_0 = ref('canvasElement3_0');
	let track3, analyser3_0, analyser3_0FrequencyBufferLength, analyser3_0TimeDomainBufferLength,
		analyser3_0FrequencyUint8Array, analyser3_0FrequencyFloat32Array, analyser3_0TimeDomainUint8Array, analyser3_0TimeDomainFloat32Array,
		canvas3_0Ctx;
	
	watch(canvasElement3_0, (newEl, oldEl) => {
		const rect = newEl.getBoundingClientRect();
		canvas3_0Ctx = newEl.getContext('2d');
		const pixelRatio = (window.devicePixelRatio || 1) / (canvas3_0Ctx.backingStorePixelRatio || 1);
		newEl.width = rect.width * pixelRatio;
		newEl.height = rect.height * pixelRatio;
	})
	
	watch(audio3Pause, (newPause) => {
		if (!newPause) media3Draw();
	})
	
	function media3ResumeEventHandel() {
		if (auer3.state === "suspended") {
			auer3.resume().then(() => {
				track3 = auer3.createMediaElementSource(audioElement3.value);
				analyser3_0 = auer3.createAnalyser();
				analyser3_0.fftSize = 1024;
				analyser3_0FrequencyBufferLength = analyser3_0.frequencyBinCount;
				analyser3_0TimeDomainBufferLength = analyser3_0.fftSize;
				analyser3_0FrequencyUint8Array = new Uint8Array(analyser3_0FrequencyBufferLength);
				analyser3_0FrequencyFloat32Array = new Float32Array(analyser3_0FrequencyBufferLength);
				analyser3_0TimeDomainUint8Array = new Uint8Array(analyser3_0TimeDomainBufferLength);
				analyser3_0TimeDomainFloat32Array = new Float32Array(analyser3_0TimeDomainBufferLength);
				track3.connect(analyser3_0);
				analyser3_0.connect(auer3.destination);
			});
		}
	}
	
	function media3Draw() {
		if (audio3Pause.value) return;
		const { width, height } = canvasElement3_0.value;
		canvas3_0Ctx.clearRect(0, 0, width, height);
		canvas3_0Ctx.fillStyle = '#fff';
		canvas3_0Ctx.strokeStyle = '#fff';
		canvas3_0Ctx.lineWidth = 1;
		
		if (audio3Type.value === 'floatFrequency') {
			analyser3_0.getFloatFrequencyData(analyser3_0FrequencyFloat32Array);
			const barWidth = width / analyser3_0FrequencyBufferLength - 1;
			for (let i = 0; i < analyser3_0FrequencyBufferLength; i++) {
				const barHeight = analyser3_0FrequencyFloat32Array[i];
				canvas3_0Ctx.save();
				canvas3_0Ctx.translate((barWidth + 1) * i, height / 2);
				canvas3_0Ctx.fillRect(-barWidth / 2, -barHeight / 2, barWidth, barHeight);
				canvas3_0Ctx.restore();
			}
		} else if (audio3Type.value === 'byteFrequency') {
			analyser3_0.getByteFrequencyData(analyser3_0FrequencyUint8Array);
			const barWidth = width / analyser3_0FrequencyBufferLength - 1;
			for (let i = 0; i < analyser3_0FrequencyBufferLength; i++) {
				const barHeight = analyser3_0FrequencyUint8Array[i];
				canvas3_0Ctx.save();
				canvas3_0Ctx.translate((barWidth + 1) * i, height / 2);
				canvas3_0Ctx.fillRect(-barWidth / 2, -barHeight / 2, barWidth, barHeight);
				canvas3_0Ctx.restore();
			}
		} else if (audio3Type.value === 'floatTimeDomain') {
			analyser3_0.getFloatTimeDomainData(analyser3_0TimeDomainFloat32Array);
			const sliceWidth = width / analyser3_0TimeDomainBufferLength;
			canvas3_0Ctx.save();
			canvas3_0Ctx.beginPath();
			canvas3_0Ctx.moveTo(0, height / 2 + analyser3_0TimeDomainFloat32Array[0] * 100);
			for (let i = 1; i < analyser3_0TimeDomainBufferLength; i++) {
				const sliceHeight = analyser3_0TimeDomainFloat32Array[i] * 100;
				canvas3_0Ctx.lineTo(sliceWidth * i, height / 2 + sliceHeight);
			}
			canvas3_0Ctx.lineTo(width, height / 2);
			canvas3_0Ctx.stroke();
			canvas3_0Ctx.restore();
		} else if (audio3Type.value === 'byteTimeDomain') {
			analyser3_0.getByteTimeDomainData(analyser3_0TimeDomainUint8Array);
			const sliceWidth = width / analyser3_0TimeDomainBufferLength;
			canvas3_0Ctx.save();
			canvas3_0Ctx.beginPath();
			canvas3_0Ctx.moveTo(0, height / 2 * analyser3_0TimeDomainUint8Array[0] / 128);
			for (let i = 1; i < analyser3_0TimeDomainBufferLength; i++) {
				const sliceHeight = analyser3_0TimeDomainUint8Array[i] / 128;
				canvas3_0Ctx.lineTo(sliceWidth * i, height / 2 * sliceHeight);
			}
			canvas3_0Ctx.lineTo(width, height / 2);
			canvas3_0Ctx.stroke();
			canvas3_0Ctx.restore();
		}
		
		requestAnimationFrame(media3Draw);
	}
	
	const audioElement4 = ref();
	const auer4 = new AudioContext();
	const audio4Type = ref('allpass');
	const audio4Frequency = ref(1000);
	const audio4Q = ref(1);
	const audio4Gain = ref(25);
	let track4, biquadFilter4_0;
	
	watch(audio4Type, (newVal) => biquadFilter4_0.type = newVal);
	watch(audio4Frequency, (newVal) => biquadFilter4_0.frequency.value = newVal);
	watch(audio4Q, (newVal) => biquadFilter4_0.Q.value = newVal);
	watch(audio4Gain, (newVal) => biquadFilter4_0.gain.value = newVal);
	
	function media4ResumeEventHandel() {
		if (auer4.state === "suspended") {
			auer4.resume().then(() => {
				track4 = auer4.createMediaElementSource(audioElement4.value);
				biquadFilter4_0 = auer4.createBiquadFilter();
				biquadFilter4_0.type = audio4Type.value;
				biquadFilter4_0.frequency.value = audio4Frequency.value;
				biquadFilter4_0.Q.value = audio4Q.value;
				biquadFilter4_0.gain.value = audio4Gain.value;
				track4.connect(biquadFilter4_0);
				biquadFilter4_0.connect(auer4.destination);
			});
		}
	}
	
	const auer5 = new AudioContext();
	const audio5Pause = ref(true);
	let source5_0;
	
	function media5ResumeEventHandel() {
		if (auer4.state === "suspended") {
			auer4.resume();
		}
	}
	
	function media5PlayWhiteNoise() {
		if (audio3Pause.value) {
			source5_0 = auer5.createBufferSource();
			const buffer = auer5.createBuffer(2, 5 * auer5.sampleRate, auer5.sampleRate);
			for (let channel = 0; channel < buffer.numberOfChannels; channel++) {
				const channelData = buffer.getChannelData(channel);
				for (let i = 0; i < channelData.length; i++) {
					channelData[i] = Math.random() * 2 - 1;
				}
			}
			source5_0.buffer = buffer;
			source5_0.connect(auer5.destination);
			source5_0.start();
			source5_0.addEventListener('ended', function() {
				audio5Pause.value = true;
			})
		} else {
			source5_0.stop();
		}
		audio5Pause.value = !audio5Pause.value;
	}
	
	function media5ResetWhiteNoise() {
		source5_0.stop();
		source5_0.disconnect();
	}
	
	const audioElement6 = ref();
	const auer6 = new AudioContext();
	const audio6PannerX = ref(0);
	const audio6PannerY = ref(0);
	const audio6PannerZ = ref(0);
	let track6, panner6_0, listener6_0;
	
	watch(audio6PannerX, (newVal) => panner6_0.positionX.value = newVal);
	watch(audio6PannerY, (newVal) => panner6_0.positionY.value = newVal);
	watch(audio6PannerZ, (newVal) => panner6_0.positionZ.value = newVal);
	
	function media6ResumeEventHandel() {
		if (auer6.state === "suspended") {
			auer6.resume();
			track6 = auer6.createMediaElementSource(audioElement6.value);
			panner6_0 = auer6.createPanner();
			panner6_0.panningModel = 'HRTF';
			panner6_0.distanceModel = 'inverse';
			panner6_0.maxDistance = 1000;
			panner6_0.refDistance = 50;
			panner6_0.rolloffFactor = 1;
			panner6_0.coneInnerAngle = 360;
			panner6_0.orientationX.value = 0;
			panner6_0.orientationY.value = 0;
			panner6_0.orientationZ.value = 1;
			panner6_0.positionX.value = audio6PannerX.value;
			panner6_0.positionY.value = audio6PannerY.value;
			panner6_0.positionZ.value = audio6PannerZ.value;
			listener6_0 = auer6.listener;
			listener6_0.forwardX.value = 0;
			listener6_0.forwardY.value = 0;
			listener6_0.forwardZ.value = -1;
			listener6_0.upX.value = 0;
			listener6_0.upY.value = 1;
			listener6_0.upZ.value = 0;
			listener6_0.positionX.value = 100;
			listener6_0.positionY.value = 0;
			listener6_0.positionZ.value = 200;
			track6.connect(panner6_0);
			panner6_0.connect(auer6.destination);
		}
	}
	
	const audioElement7 = ref();
	const auer7 = new AudioContext();
	const audio7LRChannel = ref(true);
	const audio7GainL = ref(1);
	const audio7GainR = ref(1);
	let track7, splitter7_0, merger7_0, gain7_0, gain7_1;
	
	watch(audio7GainL, newVal => gain7_0 && (gain7_0.gain.value = newVal));
	watch(audio7GainR, newVal => gain7_1 && (gain7_1.gain.value = newVal));
	
	function media7ResumeEventHandel() {
		if (auer7.state === "suspended") {
			auer7.resume();
			track7 = auer7.createMediaElementSource(audioElement7.value);
			splitter7_0 = auer7.createChannelSplitter(2);
			merger7_0 = auer7.createChannelMerger(2);
			gain7_0 = auer7.createGain();
			gain7_1 = auer7.createGain();
			gain7_0.gain.value = audio7GainL.value;
			gain7_1.gain.value = audio7GainR.value;
			track7.connect(splitter7_0);
			splitter7_0.connect(gain7_0, 0);
			splitter7_0.connect(gain7_1, 1);
			gain7_0.connect(merger7_0, 0, 0);
			gain7_1.connect(merger7_0, 0, 1);
			merger7_0.connect(auer7.destination);
		}
	}
	
	function media7ChangeLRChannel() {
		gain7_0.disconnect();
		gain7_1.disconnect();
		if (audio7LRChannel.value) {
			gain7_0.connect(merger7_0, 0, 1);
			gain7_1.connect(merger7_0, 0, 0);
		} else {
			gain7_0.connect(merger7_0, 0, 0);
			gain7_1.connect(merger7_0, 0, 1);
		}
		audio7LRChannel.value = !audio7LRChannel.value;
	}
	
</script>

<style lang="scss" scoped>
</style>